// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.

#ifndef LSPSERVER_PROTOCOL_H
#define LSPSERVER_PROTOCOL_H

#include <cstdint>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "../languageserver/index/Symbol.h"
#include "CompletionType.h"
#include "WorkSpaceSymbolType.h"
#include "cangjie/Basic/Position.h"

/**
 * According to the language service protocol to create structure
 * see https://microsoft.github.io/language-server-protocol/specifications/specification-3-16/#baseProtocol
 */
namespace ark {
enum class ErrorCode {
    PARSE_ERROR = -32700,
    INVALID_REQUEST = -32600,
    METHOD_NOT_FOUND = -32601,
    SERVER_NOT_INITIALIZED = -32002,
    UNKNOWN_ERROR_CODE = -32001,
    // Customized error code. (>= -31999 or <= -32900)
    INVALID_RENAME_FOR_MACRO_CALL_FILE = -31999
};

class MessageErrorDetail {
public:
    std::string message = "";
    ErrorCode code;

    MessageErrorDetail() : message(""), code(ErrorCode::PARSE_ERROR) {};

    MessageErrorDetail(std::string message, ErrorCode code) : message(std::move(message)), code(code) {}
    ~MessageErrorDetail() {}
};

// Sync document changes strategy for language server
enum class TextDocumentSyncKind {
    // Documents not be synced at any time.
    SK_NONE = 0,

    // Smart sync, Documents are synced using the full content on open.
    // After only incremental updates to the document.
    SK_INCREMENTAL = 2,
};

enum class HighlightKind {
    FILE_H = 1,
    MODULE_H = 2,
    NAMESPACE_H = 3,
    PACKAGE_H = 4,
    CLASS_H = 5,
    METHOD_H = 6,
    PROPERTY_H = 7,
    FIELD_H = 8,
    CONSTRUCTOR_H = 9,
    ENUM_H = 10,
    INTERFACE_H = 11,
    FUNCTION_H = 12,
    VARIABLE_H = 13,
    CONSTANT_H = 14,
    NUMBER_H = 16,
    BOOLEAN_H = 17,
    ARRAY_H = 18,
    OBJECT_H = 19,
    KEY_H = 20,
    MISSING_H = 21,
    ENUMMEMBER_H = 22,
    STRUCT_H = 23,
    EVENT_H = 24,
    OPERATOR_H = 25,
    TYPEPARAMETER_H = 26,
    COMMENT_H = 27,
    RECORD_H = 28,
    TRAIT_H = 29,
    INACTIVECODE_H
};

struct TextDocumentIdentifier {
    // The text document's URI.
    URIForFile uri;
    ~TextDocumentIdentifier() = default;
};

struct TextDocumentPositionParams {
    TextDocumentIdentifier textDocument;

    Cangjie::Position position;

    ~TextDocumentPositionParams() = default;
};

// TypeHierarchy response
struct TypeHierarchyItem {
public:
    std::string name = "";

    SymbolKind kind = SymbolKind::FILE;

    URIForFile uri {};

    Range range {};

    Range selectionRange {};

    bool isKernel{false};
    bool isChildOrSuper{true};
    lsp::SymbolID symbolId = lsp::INVALID_SYMBOL_ID;

    bool operator==(const TypeHierarchyItem &rhs) const
    {
        return name == rhs.name && uri.file == rhs.uri.file && kind == rhs.kind &&
               range == rhs.range && selectionRange == rhs.selectionRange;
    }

    bool operator!=(const TypeHierarchyItem &rhs) const
    {
        return !(rhs == *this);
    }
};

struct CallHierarchyItem : public TypeHierarchyItem {
public:
    std::string detail;
};


struct CallHierarchyOutgoingCall {
    CallHierarchyItem to;
    std::vector<Range> fromRanges;
};

bool ToJSON(const CallHierarchyOutgoingCall &iter, nlohmann::json &reply);

struct CallHierarchyIncomingCall {
    CallHierarchyItem from;
    std::vector<Range> fromRanges;
};

bool ToJSON(const CallHierarchyIncomingCall &iter, nlohmann::json &reply);

bool ToJSON(const TypeHierarchyItem &iter, nlohmann::json &reply);

bool FromJSON(const nlohmann::json &params, TypeHierarchyItem &reply);

bool ToJSON(const CallHierarchyItem &iter, nlohmann::json &reply);

bool FromJSON(const nlohmann::json &params, CallHierarchyItem &reply);

bool FromJSON(const nlohmann::json &params, TextDocumentPositionParams &reply);

enum class SignatureHelpTriggerKind {
    END = 4
};

bool FromJSON(const nlohmann::json &params, CompletionContext &reply);

struct CompletionParams : public TextDocumentPositionParams {
    CompletionContext context;
};

bool FromJSON(const nlohmann::json &params, CompletionParams &reply);

bool ToJSON(const CompletionItem &iter, nlohmann::json &reply);

struct Signatures {
    std::string label = "";
    std::vector<std::string> parameters = {};

    bool operator==(const Signatures &other) const
    {
        if (this->parameters != other.parameters) {
            return false;
        }

        if (this->label != other.label) {
            return false;
        }
        return true;
    }

    bool operator<(const Signatures &other) const
    {
        if (this->parameters.size() > other.parameters.size()) {
            return false;
        }
        if (this->parameters.size() < other.parameters.size()) {
            return true;
        }
        if (this->label > other.label) {
            return false;
        }
        return true;
    }

    Signatures(): label(""), parameters() {};
};

struct SignatureHelp {
    unsigned int activeSignature = 0;

    unsigned int activeParameter = 0;

    std::vector<Signatures> signatures = {};

    bool operator==(const SignatureHelp &other) const
    {
        if (this->signatures.size() != other.signatures.size()) {
            return false;
        }
        bool isEqual = std::tie(activeSignature, activeParameter) ==
                       std::tie(other.activeSignature, other.activeParameter);
        if (!isEqual) {
            return false;
        }
        for (size_t i = 0; i < other.signatures.size(); i++) {
            if (i >= this->signatures.size() || !(this->signatures[i] == other.signatures[i])) {
                return false;
            }
        }
        return true;
    }

    bool operator!=(const SignatureHelp &other) const
    {
        return !(*this == other);
    }
};

struct SignatureHelpContext {
    bool isRetrigger = false;
    SignatureHelp activeSignatureHelp = {};
    std::string triggerCharacter = "";

    SignatureHelpContext(): isRetrigger(false), activeSignatureHelp(), triggerCharacter("") {};
};

struct SignatureHelpParams : public TextDocumentPositionParams {
    SignatureHelpContext context;
};

bool FromJSON(const nlohmann::json &params, SignatureHelpContext &reply);

struct CompletionList {
    std::vector<CompletionItem> items{};
};

enum class FileChangeType {
    // The instruction file was created.
    CREATED = 1,
    // The instruction file was changed.
    CHANGED = 2,
    // The instruction file was deleted.
    DELETED = 3,
};

struct FileWatchedEvent {
    TextDocumentIdentifier textDocument;
    FileChangeType type;
};

struct DidChangeWatchedFilesParam {
    std::vector<FileWatchedEvent> changes;
};

bool FromJSON(const nlohmann::json &params, DidChangeWatchedFilesParam &reply);

struct RenameParams {
    TextDocumentIdentifier textDocument;

    Cangjie::Position position;

    std::string newName;
};

bool FromJSON(const nlohmann::json &params, RenameParams &reply);

bool FromJSON(const nlohmann::json &params,  SignatureHelpParams &reply);

struct TextDocumentParams {
    TextDocumentIdentifier textDocument;
};

bool FromJSON(const nlohmann::json &params, TextDocumentParams &tdReply);

struct BreakpointLocation {
    std::string uri;
    Range range;

    bool operator<(const BreakpointLocation &rhs) const
    {
        return std::tie(this->uri, this->range) < std::tie(rhs.uri, rhs.range);
    }
};

bool ToJSON(const BreakpointLocation &params, nlohmann::json &reply);

struct ExecutableRange {
    std::string uri;
    std::string projectName;
    std::string packageName;
    std::string className;
    std::string functionName;
    Range range;

    bool operator<(const ExecutableRange &rhs) const
    {
        return std::tie(this->uri, this->projectName, this->packageName, this->className,
                        this->functionName, this->range) <
        std::tie(rhs.uri, rhs.projectName, rhs.packageName, rhs.className, rhs.functionName, rhs.range);
    }
};

struct Command {
    std::string title;
    std::string command;
    std::set<ExecutableRange> arguments;
};

struct CodeLens {
    Range range;
    Command command;

    bool operator<(const CodeLens &rhs) const
    {
        return std::tie(this->range, this->command.title, this->command.command) <
        std::tie(rhs.range, rhs.command.title, rhs.command.command);
    }
};

bool ToJSON(const ExecutableRange &params, nlohmann::json &reply);

bool ToJSON(const CodeLens &params, nlohmann::json &reply);

enum class DocumentHighlightKind {
    TEXT = 1,
};

struct DocumentHighlight {
    Range range;
    DocumentHighlightKind kind = DocumentHighlightKind::TEXT;

    bool operator<(const DocumentHighlight &rhs) const
    {
        return std::tie(this->range, this->kind) < std::tie(rhs.range, rhs.kind);
    }
};

struct Hover {
    Range range;
    std::vector<std::string> markedString = {};
    Hover(): range(), markedString() {};
};

struct TextDocumentItem {
    URIForFile uri;

    std::string languageId;

    int64_t version;

    std::string text;
};

struct DidOpenTextDocumentParams {
    TextDocumentItem textDocument;
};

bool FromJSON(const nlohmann::json &params, DidOpenTextDocumentParams &reply);

struct VersionedTextDocumentIdentifier : public TextDocumentIdentifier {
    std::int64_t version = -1;
};

struct TextDocumentEdit {
    VersionedTextDocumentIdentifier textDocument;

    std::vector<TextEdit> textEdits{};
};

struct TextDocumentContentChangeEvent {
    std::optional<Range> range;

    std::optional<int> rangeLength;

    std::string text;
};

struct DidChangeTextDocumentParams {
    VersionedTextDocumentIdentifier textDocument;

    std::vector<TextDocumentContentChangeEvent> contentChanges{};
};

bool FromJSON(const nlohmann::json &params, DidChangeTextDocumentParams &reply);

struct DidCloseTextDocumentParams {
    TextDocumentIdentifier textDocument;
};

bool FromJSON(const nlohmann::json &params, DidCloseTextDocumentParams &reply);

struct DidSaveTextDocumentParams {
    TextDocumentIdentifier textDocument;
};

struct TrackCompletionParams {
    std::string label;
};

bool FromJSON(const nlohmann::json &params, TrackCompletionParams &reply);

struct SemanticTokensParams {
    TextDocumentIdentifier textDocument;
};

struct SemanticTokens {
    std::vector<int> data;
};

enum class SemanticTokenTypes {
    COMMENT_T = 0,
    KEYWORD_T = 1,
    NUMBER_T = 3,
    OPERATOR_T = 5,
    NAMESPACE_T = 6,
    TYPE_T = 7,
    STRUCT_T = 8,
    CLASS_T = 9,
    INTERFACE_T = 10,
    ENUM_T = 11,
    TYPE_PARAMETER_T = 12,
    FUNCTION_T = 13,
    PROPERTY_T = 15,
    MACRO_T = 16,
    VARIABLE_T = 17,
    LABEL_T = 19
};

bool FromJSON(const nlohmann::json &params, SemanticTokensParams &reply);

struct TextDocumentClientCapabilities {
    bool documentHighlightClientCapabilities = false;

    bool hoverClientCapabilities = false;

    bool diagnosticVersionSupport = false;

    bool documentLinkClientCapabilities = false;

    bool typeHierarchyCapabilities = false;
};

struct ClientCapabilities {
    TextDocumentClientCapabilities textDocumentClientCapabilities;
};

struct InitializeParams {
    std::string rootPath = "";

    URIForFile rootUri;

    nlohmann::json initializationOptions;

    ClientCapabilities capabilities;

    InitializeParams(): rootPath("") {};
};

bool FromJSON(const nlohmann::json &params, InitializeParams &reply);

bool FetchTextDocument(const nlohmann::json &textDocument, InitializeParams &reply);

bool FromJSON(const nlohmann::json &params, TextDocumentIdentifier &reply);

struct SemanticHighlightToken {
    HighlightKind kind = HighlightKind::MISSING_H;
    Range range;
};

struct DiagnosticRelatedInformation {
    Location location;

    std::string message;
};

struct CodeAction;

struct DiagFix {
    bool isAutoImport{false};
};

struct DiagnosticToken {
    Range range;

    int severity = 0;

    int code;

    std::string source = "";

    std::string message = "";

    std::optional<std::vector<DiagnosticRelatedInformation>> relatedInformation{};

    std::optional<int> category{};

    std::optional<std::vector<CodeAction>> codeActions{};

    std::optional<DiagFix> diaFix = DiagFix{false};

    DiagnosticToken()
        : range(), severity(0), code(0), source(""), message(""), relatedInformation() {};
};

// In a time-consuming complete scenario, the tip is displayed, indicating that the user needs to wait.
struct CompletionTip {
    // The URI for which completion tip is display.
    URIForFile uri;
    // the position of tip
    Cangjie::Position position = {0, -1, -1};
    // tip message
    std::string tip;
};

bool ToJSON(const DiagnosticToken &iter, nlohmann::json &reply);

bool ToJSON(const DiagnosticRelatedInformation &info, nlohmann::json &reply);

struct DiagnosticCompare {
    bool operator()(const DiagnosticToken &lhs, const DiagnosticToken &rhs) const
    {
        return std::tie(lhs.range, lhs.message) < std::tie(rhs.range, rhs.message);
    }
};
struct PublishDiagnosticsParams {
    URIForFile uri;

    std::vector<DiagnosticToken> diagnostics;

    std::optional<int64_t> version;
};
bool ToJSON(const PublishDiagnosticsParams &params, nlohmann::json &reply);

struct WorkspaceEdit {
    std::map<std::string, std::vector<TextEdit>> changes;
};
bool ToJSON(const WorkspaceEdit &params, nlohmann::json &reply);

struct CodeAction {
    std::string title = "";

    std::string kind = "";

    std::optional<std::vector<DiagnosticToken>> diagnostics;

    bool isPreferred = false;

    std::optional<WorkspaceEdit> edit{};

    std::optional<Command> command;
};
bool ToJSON(const CodeAction &params, nlohmann::json &reply);

bool ToJSON(const TextDocumentEdit &params, nlohmann::json &reply);

struct DocumentLinkParams {
    TextDocumentIdentifier textDocument;
};

bool FromJSON(const nlohmann::json &params, DocumentLinkParams &reply);

struct DocumentSymbolParams {
    TextDocumentIdentifier textDocument;
};

struct DocumentSymbol {
    std::string name;
    std::string detail;
    SymbolKind kind;
    Range range;
    Range selectionRange;
    std::vector<DocumentSymbol> children;

    bool operator<(const DocumentSymbol &other) const
    {
        return std::tie(selectionRange, range, kind, name) <
               std::tie(other.selectionRange, other.range, other.kind, other.name);
    }

    bool operator==(const DocumentSymbol &other) const
    {
        if (this->children.size() != other.children.size()) {
            return false;
        }
        bool isEqual = std::tie(kind, name, detail, selectionRange, range) ==
                       std::tie(other.kind, other.name, other.detail, other.selectionRange, other.range);
        if (!isEqual) {
            return false;
        }
        for (size_t i = 0; i < other.children.size(); i++) {
            if (!(this->children[i] == other.children[i])) {
                return false;
            }
        }
        return true;
    }

    bool operator!=(const DocumentSymbol &other) const
    {
        return !(*this == other);
    }
};

bool FromJSON(const nlohmann::json &params, DocumentSymbolParams &dsReply);

bool ToJSON(const DocumentSymbol &item, nlohmann::json &result);

class MessageHeaderEndOfLine {
public:
    static std::string& GetEol()
    {
        return eol;
    }

    static void SetEol(const std::string& str)
    {
        eol = str;
    }

    static bool GetIsDeveco()
    {
        return isDeveco;
    }

    static void SetIsDeveco(bool flag)
    {
        isDeveco = flag;
    }
private:
    static std::string eol;
    static bool isDeveco;
};
} // namespace ark
#endif // LSPSERVER_PROTOCOL_H
